import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;

public class SocketConnections extends Thread {
	ServerSocket sersoc = null;
	int serverPortNumber;
	Thread receive[];
	Thread send[];
	FileProperties fileProp;
	PeerData peerData;
	SharedMessageQueue[] sharedMsg;
	CountDownLatch countDown;
	Logger logger;
	FileHandler fileHandler;
	Socket socArray[];

	public SocketConnections(FileProperties fileProp, PeerData peerData,
			SharedMessageQueue[] sharedMsg, CountDownLatch countDown,
			Logger logger) {
		// TODO Auto-generated constructor stub
		this.fileProp = fileProp;
		this.peerData = peerData;
		this.sharedMsg = sharedMsg;
		this.countDown = countDown;
		this.logger = logger;
		socArray = new Socket[fileProp.peerIDArray.length];

		serverPortNumber = getPortNo(peerData.getPeerID());
		send = new Thread[fileProp.peerIDArray.length];
		receive = new Thread[fileProp.peerIDArray.length];

		// Initialize Filehandler
		fileHandler = new FileHandler(fileProp.getFileName(),
				fileProp.getFileSize(), fileProp.getPieceSize());

	}

	private int getPortNo(int peerID) {
		return fileProp.peerPortNumbers[peerData.getPeerNum(peerID)];
	}

	boolean startServerSoc() {
		try {
			sersoc = new ServerSocket(serverPortNumber);
			return true;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	
	class ConnectToLower extends Thread {

		public void run() {
			try {
				Thread.sleep(5000);
				for (int i = 0; i < fileProp.peerIDArray.length; i++) {
					if (fileProp.peerIDArray[i] < peerData.getPeerID()) {
						socArray[i] = new Socket(fileProp.peerHostNames[i],
								fileProp.peerPortNumbers[i]);
						logger.log("Peer " + peerData.getPeerID()
								+ " makes a connection to Peer "
								+ fileProp.peerIDArray[i] + ". ");
						System.out.println("Peer " + peerData.getPeerID()
								+ " makes a connection to Peer "
								+ fileProp.peerIDArray[i] + ". ");
					}
					/************** Initializing shared object queue for this connection **********/
					sharedMsg[i] = new SharedMessageQueue();
					sharedMsg[i].peerID = fileProp.peerIDArray[i];

				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	boolean setSocketConnections() {
		try {
			/************ generate Streams ************************/
			ObjectOutputStream objOut = null;
			ObjectInputStream objIn = null;

			ConnectToLower lowerConnector = new ConnectToLower();
			lowerConnector.start();
			for (int i = 0; i < fileProp.peerIDArray.length; i++) {
				if (fileProp.peerIDArray[i] > peerData.getPeerID()) {
					socArray[i] = sersoc.accept(); // //Receiving from peers
													// whose ID>peerID
					logger.log("Peer " + peerData.getPeerID()
							+ " is connected from Peer "
							+ fileProp.peerIDArray[i] + ". ");
					System.out.println("Peer " + peerData.getPeerID()
							+ " is connected from Peer "
							+ fileProp.peerIDArray[i] + ". ");
				} else {
					continue;
				}

				/************** Initializing shared object queue for this connection **********/
				sharedMsg[i] = new SharedMessageQueue();
				sharedMsg[i].peerID = fileProp.peerIDArray[i];

			}
			
			lowerConnector.join();

			for (int i = 0; i < fileProp.peerIDArray.length; i++) {
				if (fileProp.peerIDArray[i] == peerData.getPeerID()) {
					continue;
				}
				objOut = new ObjectOutputStream(socArray[i].getOutputStream());
				objIn = new ObjectInputStream(socArray[i].getInputStream());

				/************ receiver thread from this peer to all peers ********/
				receive[i] = new ReceiverThread(objIn, peerData, sharedMsg,
						countDown, fileProp.peerIDArray[i], logger,
						fileHandler, "Receiver:" + i);
				receive[i].start();

				/*********** Sender thread from this peer to all peer *********/
				send[i] = new SenderThread(objOut, sharedMsg[i], "Sender:" + i,
						peerData);
				send[i].start();

			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

	}

	public void joinSenderReceiverThreads(){
		for (int i = 0; i < fileProp.peerIDArray.length; i++) {
			if (fileProp.peerIDArray[i] != peerData.getPeerID()) {
				try {
					receive[i].join();
					send[i].join();
					System.err.println("Send and Receive joined for "+fileProp.peerIDArray[i]+" at "+peerData.getPeerID());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					System.err.println("In socket Connections");
					e.printStackTrace();
				}
				if(!socArray[i].isClosed()){
					try {
						socArray[i].close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						System.err.println("In socket Connections");
						e.printStackTrace();
					}
				}
				
			}
		}
		
	}

	public void run() {
		if (!startServerSoc()) {
			// ERROR MEssage print
			System.out.println("Server setup failed for + "
					+ peerData.getPeerID() + " + on port:" + serverPortNumber);
		} else {
			if (!setSocketConnections()) {
				// ERROR Message print				
			}
			else{
				joinSenderReceiverThreads();
			}
		}
	}
}
